#include "Window.h"
#include "Mouse.h"
#include "Keyboard.h"
#include "OpenGL.h"
#include "Application.h"

Window Window::mainWindow;

Window& Window::MainWindow(void)
{
	return mainWindow;
}
void Window::SetMainWindow(Window& mainWindow)
{
	mainWindow = mainWindow;
}

HWND Window::Wnd(void)
{
	return wnd;
}
void Window::SetWnd(HWND wnd)
{
	this->wnd = wnd;
}

HDC Window::Dc(void)
{
	return dc;
}
void Window::SetDc(HDC dc)
{
	this->dc = dc;
}

HGLRC Window::Rc(void)
{
	return rc;
}
void Window::SetRc(void)
{
	this->rc = rc;
}

int Window::Width(void)
{
	return width;
}
void Window::SetWidth(int widht)
{
	this->width = width;
}

int Window::Height(void)
{
	return height;
}
void Window::SetHeight(int height)
{
	this->height = height;
}

bool Window::Active(void)
{
	return active;
}
void Window::SetActive(bool active)
{
	this->active = active;
}

Window::Window()
{
	this->dc = NULL;
	this->rc = NULL;
	this->wnd = NULL;
	this->height = 0;
	this->width = 0;
}

Window::Window(wchar_t *title, int width, int height)
{
	Create(title, width, height);
}

Window::~Window()
{
	Destroy();
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{	
	return Window::MainWindow().WindowProc(hWnd, uMsg, wParam, lParam);		
}

bool Window::Create(wchar_t *title, int width, int height)
{	
	className = new wchar_t[3];
	className[0] = 'A';
	className[1] = 'I';
	className[2] = 0;
	GLuint		PixelFormat;			// Holds The Results After Searching For A Match
	WNDCLASS	wc;						// Windows Class Structure
	DWORD		dwExStyle;				// Window Extended Style
	DWORD		dwStyle;				// Window Style
	RECT		WindowRect;				// Grabs Rectangle Upper Left / Lower Right Values
	WindowRect.left = (long)0;			// Set Left Value To 0
	WindowRect.right = (long)width;		// Set Right Value To Requested Width
	WindowRect.top = (long)0;			// Set Top Value To 0
	WindowRect.bottom = (long)height;	// Set Bottom Value To Requested Height

	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraw On Size, And Own DC For Window.
	wc.lpfnWndProc		= (WNDPROC) WndProc;					// WndProc Handles Messages
	wc.cbClsExtra		= 0;									// No Extra Window Data
	wc.cbWndExtra		= 0;									// No Extra Window Data
	wc.hInstance		= Application::Instance();				// Set The Instance
	wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);			// Load The Default Icon
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);			// Load The Arrow Pointer
	wc.hbrBackground	= NULL;									// No Background Required For GL
	wc.lpszMenuName		= NULL;									// We Don't Want A Menu
	wc.lpszClassName	= className;								// Set The Class Name

	if (!RegisterClass(&wc))									// Attempt To Register The Window Class
	{
		MessageBox(NULL, L"Failed to register the window class.", L"Error", MB_OK | MB_ICONEXCLAMATION);
		return false;											// Return FALSE
	}
		
	dwExStyle = WS_EX_APPWINDOW;							// Window Extended Style
	dwStyle = WS_POPUP;										// Windows Style
	ShowCursor(FALSE);
	SetCursorPos(width >> 1, height >> 1);
	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);		// Adjust Window To True Requested Size

	// Create The Window
	if (!(wnd = CreateWindowEx(dwExStyle,							// Extended Style For The Window
								className,							// Class Name
								title,								// Window Title
								dwStyle |							// Defined Window Style
								WS_CLIPSIBLINGS |					// Required Window Style
								WS_CLIPCHILDREN,					// Required Window Style
								0, 0,								// Window Position
								WindowRect.right-WindowRect.left,	// Calculate Window Width
								WindowRect.bottom-WindowRect.top,	// Calculate Window Height
								NULL,								// No Parent Window
								NULL,								// No Menu
								Application::Instance(),			// Instance
								NULL)))								// Dont Pass Anything To WM_CREATE
	{
		Destroy();		
		MessageBox(NULL, L"Window creation error.", L"Error", MB_OK | MB_ICONEXCLAMATION);
		return false;								// Return FALSE
	}

	static	PIXELFORMATDESCRIPTOR pfd=				// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,											// Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		32,											// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		1,											// No Alpha Buffer
		0,											// Shift Bit Ignored
		0,											// No Accumulation Buffer
		0, 0, 0, 0,									// Accumulation Bits Ignored
		32,											// 24Bit Z-Buffer (Depth Buffer)  
		0,											// No Stencil Buffer
		0,											// No Auxiliary Buffer
		PFD_MAIN_PLANE,								// Main Drawing Layer
		0,											// Reserved
		0, 0, 0										// Layer Masks Ignored
	};
	
	if (!(dc = GetDC(wnd)))							// Did We Get A Device Context?
	{
		Destroy();		
		MessageBox(NULL, L"Can't create GL device context.", L"Error", MB_OK | MB_ICONEXCLAMATION);
		return false;								// Return FALSE
	}

	if (!(PixelFormat = ChoosePixelFormat(dc, &pfd)))	// Did Windows Find A Matching Pixel Format?
	{
		Destroy();		
		MessageBox(NULL, L"Can't find suitable PixelFormat.", L"Error", MB_OK | MB_ICONEXCLAMATION);
		return false;								// Return FALSE
	}

	if(!SetPixelFormat(dc,PixelFormat,&pfd))		// Are We Able To Set The Pixel Format?
	{
		Destroy();		
		MessageBox(NULL, L"Can't set the PixelFormat.", L"Error", MB_OK | MB_ICONEXCLAMATION);
		return false;								// Return FALSE
	}

	if (!(rc = wglCreateContext(dc)))				// Are We Able To Get A Rendering Context?
	{
		Destroy();		
		MessageBox(NULL, L"Can't create GL rendering context.", L"Error", MB_OK | MB_ICONEXCLAMATION);
		return false;								// Return FALSE
	}

	if(!wglMakeCurrent(dc, rc))					// Try To Activate The Rendering Context
	{
		Destroy();		
		MessageBox(NULL, L"Can't activate the GL rendering context.", L"Error", MB_OK | MB_ICONEXCLAMATION);
		return false;								// Return FALSE
	}

	ShowWindow(wnd,SW_SHOW);						// Show The Window
	SetForegroundWindow(wnd);						// Slightly Higher Priority
	SetFocus(wnd);									// Sets Keyboard Focus To The Window

	return true;									// Success
}

bool Window::Destroy()
{	
	if (rc)											// Do We Have A Rendering Context?
	{
		if (!wglMakeCurrent(NULL, NULL))					// Are We Able To Release The DC And RC Contexts?
		{
			MessageBox(NULL, L"Release of DC and RC failed.", L"Shutdown Error", MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(rc))						// Are We Able To Delete The RC?
		{
			MessageBox(NULL, L"Release rendering context failed.", L"Shutdown Error", MB_OK | MB_ICONINFORMATION);
		}
		rc = NULL;										// Set RC To NULL
	}

	if (dc && !ReleaseDC(wnd, dc))					// Are We Able To Release The DC
	{
		MessageBox(NULL, L"Release device context failed.", L"Shutdown Error", MB_OK | MB_ICONINFORMATION);
		dc = NULL;										// Set DC To NULL
	}

	if (wnd && !DestroyWindow(wnd))					// Are We Able To Destroy The Window?
	{
		MessageBox(NULL, L"Could not release window.", L"Shutdown Error", MB_OK | MB_ICONINFORMATION);
		wnd = NULL;										// Set hWnd To NULL
	}

	if (!UnregisterClass(className, Application::Instance()))			// Are We Able To Unregister Class
	{
		MessageBox(NULL, L"Could not unregister class.", L"Shutdown Error", MB_OK | MB_ICONINFORMATION);		
	}
	return true;
	delete[] className;
	className = NULL;
}

LRESULT CALLBACK Window::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)									// Check For Windows Messages
	{
		case WM_ACTIVATE:							// Watch For Window Activate Message
		{
			if (!HIWORD(wParam))					// Check Minimization State			
				active = true;						// Program Is Active			
			else			
				active = false;						// Program Is Active			

			return 0;								// Return To The Message Loop
		}

		case WM_SYSCOMMAND:
		{
			switch (wParam)
			{
				case SC_SCREENSAVE:
				case SC_MONITORPOWER:
					return 0;
			}
			break;
		}

		case WM_CLOSE:								// Did We Receive A Close Message?
		{
			PostQuitMessage(0);						// Send A Quit Message
			return 0;								// Jump Back
		}

		case WM_KEYDOWN:							// Is A Key Being Held Down?
		{
			Keyboard::SetKeyPressed((unsigned int)wParam);
			return 0;								// Jump Back
		}

		case WM_KEYUP:								// Has A Key Been Released?
		{
			Keyboard::SetKeyReleased((unsigned int)wParam);			
			return 0;								// Jump Back
		}

		case WM_SIZE:								// Resize The OpenGL Window
		{
			OpenGL::Resize(LOWORD(lParam),HIWORD(lParam));  // LoWord=Width, HiWord=Height						
			return 0;								// Jump Back
		}
		/*
		case WM_PAINT:
		{		
			OpenGL::Render();						
			return 0;
		}
		*/
		case WM_MOUSEMOVE:
		{
			Mouse::SetX(GET_X_LPARAM(lParam));
			Mouse::SetY(GET_Y_LPARAM(lParam));
			return 0;
		}
		case WM_LBUTTONDOWN:
		{
			if(wParam & MK_LBUTTON)
				Mouse::SetLeft(true);
			else
				Mouse::SetLeft(false);
			return 0;
		}
		case WM_LBUTTONUP:
		{
			if(wParam & MK_LBUTTON)
				Mouse::SetLeft(true);
			else
				Mouse::SetLeft(false);
			return 0;
		}
		case WM_RBUTTONDOWN:
		{
			if(wParam & MK_RBUTTON) 
			{
				Mouse::SetRight(true);
				SetCapture(hWnd);
			}
			else 
			{
				Mouse::SetRight(false);
				ReleaseCapture();
			}
			return 0;
		}
		case WM_RBUTTONUP:
		{
			if(wParam & MK_RBUTTON) 
			{
				Mouse::SetRight(true);
				SetCapture(hWnd);
			}
			else 
			{
				Mouse::SetRight(false);
				ReleaseCapture();
			}
			return 0;
		}
		case WM_CANCELMODE:
		{
			ReleaseCapture();
			return 0;
		}		
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);	
}